package models

import (
	"errors"
	"time"

	"gitee.com/sansaniot/ssiot-core/facade/env"
	"gitee.com/sansaniot/ssiot-core/sdk/model"
	"gorm.io/datatypes"
	"gorm.io/gorm"

	"ssdevice/common/constant"
	cDto "ssdevice/common/dto"
	"ssdevice/common/enums"
	"ssdevice/common/utils"
	"ssdevice/internal/device/models/query"
)

type SysDev struct {
	Id           string         `json:"id" gorm:"primaryKey;type:varchar(255);comment:id"`
	Sn           string         `json:"sn" gorm:"type:varchar(255);comment:设备序列号"`
	Name         string         `json:"name" gorm:"type:varchar(255);comment:设备名称"`
	DeptId       string         `json:"deptId" gorm:"type:varchar(255);comment:分组id"`
	OnlineStatus string         `json:"onlineStatus" gorm:"type:varchar(255);comment:在线状态"`
	Type         string         `json:"type" gorm:"type:varchar(255);comment:类型"`
	ProductModel string         `json:"productModel" gorm:"type:varchar(255);comment:产品编码"`
	GatewaySn    string         `json:"gatewaySn" gorm:"type:varchar(255);comment:网关SN"`
	AlarmStatus  bool           `json:"alarmStatus" gorm:"type:bool;comment:是否告警"`
	Location     datatypes.JSON `json:"location" gorm:"type:jsonb;comment:地址经纬度"`

	CreateTime   *cDto.LocalTime `json:"createTime" gorm:"type:timestamptz(6);comment:创建时间"`
	UpdateTime   *cDto.LocalTime `json:"updateTime" gorm:"type:timestamptz(6);comment:更新时间"`
	LastLiveTime *cDto.LocalTime `json:"lastLiveTime" gorm:"type:timestamptz(6);comment:末次交互时间"`
	RegisterTime *cDto.LocalTime `json:"registerTime" gorm:"type:timestamptz(6);comment:注册时间"`
	LogoutTime   *cDto.LocalTime `json:"logoutTime" gorm:"type:timestamptz(6);comment:登出时间"`

	model.Model

	DeptName string                 `json:"deptName" gorm:"-"`
	SvcInfo  map[string]interface{} `json:"svcInfo" gorm:"-"`
}

func (SysDev) TableName() string {
	return "sys_dev"
}

func (e *SysDev) Generate() interface{} {
	o := *e
	return &o
}

func (e *SysDev) GetId() interface{} {
	return e.Id
}

func (e *SysDev) FindDeviceList(req *query.SysDevQuery) (err error, result interface{}) {
	list := make([]SysDev, 0)
	var count int64
	if req.PageIndex > 0 && req.PageSize > 0 {
		err = e.FindPage(&list, &count,
			cDto.MakeCondition(req.GetNeedSearch()),
			cDto.OrderDest("create_time", true, req.OrderBy),
			cDto.Paginate(req.GetPageSize(), req.GetPageIndex()),
		)
		groupIdName, _ := utils.GroupIdName(e.Orm)
		for i := range list {
			if len(list[i].DeptId) > 0 {
				if deptName, ok := groupIdName[list[i].DeptId]; ok {
					list[i].DeptName = deptName
				}
			}
		}
		result = &cDto.Page{
			Records:   list,
			Total:     count,
			PageSize:  req.GetPageSize(),
			PageIndex: req.GetPageIndex(),
			Pages:     (count + int64(req.GetPageSize()) - 1) / int64(req.GetPageSize()),
			Other:     nil,
		}
	} else {
		err = e.FindList(&list,
			cDto.MakeCondition(req.GetNeedSearch()),
			cDto.OrderDest("create_time", true, req.OrderBy),
		)
		groupIdName, _ := utils.GroupIdName(e.Orm)
		for i := range list {
			if len(list[i].DeptId) > 0 {
				if deptName, ok := groupIdName[list[i].DeptId]; ok {
					list[i].DeptName = deptName
				}
			}
		}
		result = list
	}
	if req.Type == enums.DevTypeOfSoftGateway {
		if err = fillDeviceBizField(e.Orm, &list); err != nil {
			return
		}
	}
	return
}

func (e *SysDev) FindDevice(req *query.SysDevQuery) (err error, one SysDev) {
	err = e.FindOne(&one, cDto.MakeCondition(req.GetNeedSearch()))
	if len(one.Id) > 0 {
		if len(one.DeptId) > 0 {
			groupIdName, _ := utils.GroupIdName(e.Orm, []string{one.DeptId})
			if deptName, ok := groupIdName[one.DeptId]; ok {
				one.DeptName = deptName
			}
		}
		if req.Type == enums.DevTypeOfSoftGateway {
			svc := &SysSvc{}
			svc.Model.Orm = e.Orm
			if err = svc.GetByInstanceId(one.Sn); err != nil {
				if !errors.Is(err, gorm.ErrRecordNotFound) {
					return
				}
				err = nil
			}
			if len(svc.Id) > 0 {
				one.SvcInfo = utils.DataToMap(svc)
			}
		}
	}
	return
}

func (e *SysDev) AddDevice(addDev *SysDev) (err error) {
	var count int64
	if err = e.FindCount(&SysDev{}, &count, cDto.MakeCondition(query.SysDevQuery{IdSn: addDev.Sn})); err != nil {
		return
	}
	if count > 0 {
		err = errors.New("无法添加重复的设备SN")
		return
	}
	if err = e.Add(addDev); err != nil {
		return
	}
	return
}

func (e *SysDev) ModDevice(modFields map[string]interface{}) error {
	if len(modFields) == 0 {
		return nil
	}
	if err := e.FindOne(e, cDto.MakeCondition(query.SysDevQuery{IdSn: e.Id})); err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
		return err
	}
	modFields["update_time"] = time.Now()
	if err := e.ModMap(e, modFields); err != nil {
		return err
	}
	if len(e.Sn) > 0 && modFields["dept_id"] != nil {
		_ = env.Cache.HashSet(constant.DeviceDeptCache, map[string]interface{}{e.Sn: modFields["dept_id"].(string)})
	}
	return nil
}

func (e *SysDev) UpdateOnlineStatus(sn string, modMap map[string]interface{}) error {
	return e.ModMap(e, modMap, cDto.MakeCondition(query.SysDevQuery{Sn: sn}))
}

func fillDeviceBizField(tx *gorm.DB, list *[]SysDev) error {
	if len(*list) == 0 {
		return nil
	}
	snList := make([]string, 0)
	for _, v := range *list {
		snList = append(snList, v.Sn)
	}
	svc := &SysSvc{}
	svc.Model.Orm = tx
	svcList, err := svc.FindSvcList(&query.SysSvcQuery{InstanceIdIn: snList})
	if err != nil {
		return err
	}
	svcMap := make(map[string]SysSvc)
	for i, v := range svcList {
		svcMap[v.InstanceId] = svcList[i]
	}
	for i, v := range *list {
		dev := &(*list)[i]
		if serve, ok := svcMap[v.Sn]; ok {
			dev.SvcInfo = utils.DataToMap(serve)
		}
	}
	return nil
}

func (e *SysDev) ExistDevice(idSn string) (bool, error) {
	var count int64
	if err := e.FindCount(e, &count, cDto.MakeCondition(query.SysDevQuery{IdSn: idSn})); err != nil {
		return false, err
	}
	return count > 0, nil
}

func (e *SysDev) FindDeviceBySn(sn string) (dev SysDev, err error) {
	if err = e.FindOne(&dev, cDto.MakeCondition(query.SysDevQuery{Sn: sn})); err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			err = nil
			return
		}
	}
	return
}
